#include <string>
#include <cmath>
#include <algorithm>
#include <functional>

#include <tr1/memory>

#include <boost/foreach.hpp>
#include <boost/bind.hpp>

#include "page_vector.h"
#include "query_vector.h"
#include "page_ranking.h"

namespace jsearch
{

struct page_ranking::Impl
{
		Impl( std::tr1::shared_ptr<page_vector>& pv,
		      std::tr1::shared_ptr<query_vector>& qv )
		{
			page_vec=pv;
			query_vec=qv;
		}

		bool  ranking()
		{
			prio_pages.clear();

			std::set<int> pset = page_vec->get_page_id_set();

			BOOST_FOREACH(int i,pset) {
				prio_pages.push_back( prio_page(i, similarity(i)) );
			}	  

			using boost::bind;
			sort( prio_pages.begin(), 
				  prio_pages.end(),
				  bind(std::greater<double>(), 
					  bind(&prio_page::similarity,_1),
					  bind(&prio_page::similarity,_2)) );

			return true;
		}

		std::vector<prio_page> get_prio_pages()
		{
			std::vector<prio_page> ppg=prio_pages;
			return ppg;
		}


	private:
		double similarity(int page_id)
		{
			long double ssp = 0.0;
			long double ssq = 0.0;
			long double sum3 = 0.0;

			typedef std::tr1::shared_ptr<std::vector<std::string> > SPTRSV;
			SPTRSV terms = query_vec->get_terms();

			if (terms->size()==1)
				return  page_vec->get_weight(*terms->begin(),page_id);

			long double qw=0.0;
			long double tw=0.0;

			BOOST_FOREACH(std::string& s,*terms) {
				qw =(query_vec->get_freq(s))*(page_vec->get_idf(s));
				tw =page_vec->get_weight(s, page_id);

				sum3 +=  tw*qw;
				ssp  +=  tw*tw;
				ssq  +=  qw*qw;
			}

			long double q = sqrtl(ssp)*sqrtl(ssq);
			if( q == 0.0)
				return 0.0;

			return (sum3/q);
		}

	private:
		std::vector<prio_page>              prio_pages;
		std::tr1::shared_ptr<page_vector>   page_vec;
		std::tr1::shared_ptr<query_vector>  query_vec;
};


//============================================================
// member function
//============================================================

page_ranking::page_ranking(std::tr1::shared_ptr<page_vector>& pv,
	                     	std::tr1::shared_ptr<query_vector>& qv)
:pimpl( new Impl(pv, qv))
{ }

bool  page_ranking::ranking()
{
	return pimpl->ranking();
}

std::vector<prio_page> page_ranking::get_prio_pages()
{
	return pimpl->get_prio_pages();
}

}//end namespace
